using System;
using System.Collections.Generic;
using System.Linq;
using CodePlex.DnD4eCampaignManager.DnD4e;
using CodePlex.DnD4eCampaignManager.DnD4e.Interfaces;
using CodePlex.DnD4eCampaignManager.Presentation.ViewInterfaces;
using CodePlex.DnD4eCampaignManager.Repositories;

namespace CodePlex.DnD4eCampaignManager.Presentation
{
    public class CombatEncounterPresenter
    {
        private readonly IMonsterRepository _monsterRepository;
        private readonly IPlayerCharacterRepository _playerCharacterRepository;
        private readonly Random _random;
        private readonly ICombatEncounterView _view;
        public ICombatant PreviouslySelectedCombatant;
        private List<ICombatant> _combatants;
        private ICombatant _currentCombatant;
        private bool _updatingCombatants;

        public CombatEncounterPresenter(ICombatEncounterView view, IPlayerCharacterRepository playerCharacterRepository,
                                        IMonsterRepository monsterRepository)
        {
            _combatants = new List<ICombatant>();
            _random = new Random();
            _view = view;
            _playerCharacterRepository = playerCharacterRepository;
            _monsterRepository = monsterRepository;
            _view.Combatants = _combatants.ToArray();
        }

        public void AddMonster()
        {
            IMonster selectedMonster = _view.SelectedAvailableMonster;
            IMonster monsterFromRepository = _monsterRepository.GetByName(selectedMonster.Name);
            AddMonster(monsterFromRepository);
            RefreshListOfCombatants();
        }

        public void AddMultipleMonsters(int numberToAdd)
        {
            string monsterName = _view.SelectedAvailableMonster.Name;
            for ( int numberAdded = 0; numberAdded < numberToAdd; numberAdded++ )
            {
                IMonster monster = _monsterRepository.GetByName(monsterName);
                AddMonster(monster);
            }
            RefreshListOfCombatants();
        }

        public void AddMultipleMonstersWithSameInitiative(int numberToAdd)
        {
            int d20Result = _random.Next(1, 20);
            string monsterName = _view.SelectedAvailableMonster.Name;
            for ( int numberAdded = 0; numberAdded < numberToAdd; numberAdded++ )
            {
                IMonster monster = _monsterRepository.GetByName(monsterName);
                monster.InitiativeRoll = d20Result;
                AddMonster(monster);
            }
            RefreshListOfCombatants();
        }

        public void AdvanceCombatToNextCombatant()
        {
            SortCombatants();
            if ( _combatants.Count > 0 )
            {
                if ( _currentCombatant == null )
                {
                    _currentCombatant = _combatants[0];
                }
                else
                {
                    int index = _combatants.IndexOf(_currentCombatant) + 1;
                    _currentCombatant = index < _combatants.Count ? _combatants[index] : _combatants[0];
                }
            }
            else
            {
                _currentCombatant = null;
            }
            _view.CurrentCombatant = _currentCombatant;
        }

        public void DamageCurrentCombatant(int pointsOfDamage)
        {
            PreviouslySelectedCombatant.Damage(pointsOfDamage);
            RefreshListOfCombatants();
        }

        public void DisplayAvailableMonsters()
        {
            List<ICombatant> combatants = new List<ICombatant>();
            foreach ( string monsterName in _monsterRepository.GetAllNames() )
            {
                IMonster monster = _monsterRepository.GetByName(monsterName);
                if ( monster != null )
                {
                    combatants.Add(monster);
                }
            }
            ICombatant[] combatantsArray = ( from combatant in combatants orderby combatant.Name select combatant ).ToArray();
            _view.AvailableMonsters = combatantsArray;
        }

        public void DisplayAvailablePlayerCharacters()
        {
            _combatants = ( from combatant in _combatants where typeof ( PlayerCharacter ) != combatant.GetType() select combatant ).ToList();
            foreach ( string playerCharacterName in _playerCharacterRepository.GetAllNames() )
            {
                ICombatant playerCharacter = _playerCharacterRepository.GetByName(playerCharacterName);
                _combatants.Add(playerCharacter);
            }
            RefreshViewCombatants();
        }


        public void HealCurrentCombatant(int pointsOfHealing)
        {
            PreviouslySelectedCombatant.Heal(pointsOfHealing);
            RefreshListOfCombatants();
        }

        public void RefreshListOfCombatants()
        {
            ICombatant combatant = _view.SelectedCombatant;
            _updatingCombatants = true;
            RefreshViewCombatants();
            if ( _combatants.Contains(combatant) )
            {
                _view.SelectedCombatant = combatant;
                UpdateViewWithCombatantData(combatant);
            }
            else
            {
                _view.DeselectCombatant();
                UpdateViewWithCombatantData(new Monster(new Abilities(), new Defenses(), new HitPoints(), new Movement(), new Senses(),
                                                        new Initiative()));
            }
            _updatingCombatants = false;
        }

        public void RemoveSelectedCombatant()
        {
            _combatants.Remove(_view.SelectedCombatant);
            RefreshListOfCombatants();
        }

        public void SaveChangesToPreviouslySelectedCombatant()
        {
            if ( PreviouslySelectedCombatant != null )
            {
                PreviouslySelectedCombatant.Conditions = _view.SelectedCombatantConditions;
                PreviouslySelectedCombatant.HitPointsCurrent = _view.SelectedCombatantCurrentHitPoints;
                PreviouslySelectedCombatant.HitPointsTemporary = _view.SelectedCombatantTemporaryHitPoints;
                PreviouslySelectedCombatant.InitiativeRoll = _view.SelectedCombatantInitiativeScore - PreviouslySelectedCombatant.InitiativeModifier;
                PlayerCharacter playerCharacter = PreviouslySelectedCombatant as PlayerCharacter;
                if ( playerCharacter != null )
                {
                    playerCharacter.HealingSurgesCurrent = _view.SelectedCombatantCurrentHealingSurges;
                    _playerCharacterRepository.Save(playerCharacter);
                }
            }
        }

        public void SelectCombatant()
        {
            if ( _updatingCombatants )
            {
                return;
            }
            SaveChangesToPreviouslySelectedCombatant();
            UpdateViewWithNewlySelectedCombatant();
            RefreshListOfCombatants();
        }

        public void SpendHealingSurge()
        {
            ICombatant previouslySelectedCombatant = PreviouslySelectedCombatant;
            if ( typeof ( PlayerCharacter ) == previouslySelectedCombatant.GetType() )
            {
                ( (PlayerCharacter) previouslySelectedCombatant ).SpendHealingSurge();
                RefreshListOfCombatants();
            }
        }

        public void UpdateViewWithNewlySelectedCombatant()
        {
            ICombatant selectedCombatant = _view.SelectedCombatant;
            if ( PreviouslySelectedCombatant == selectedCombatant || selectedCombatant == null )
            {
                return;
            }
            UpdateViewWithCombatantData(selectedCombatant);
        }

        private void AddMonster(IMonster monster)
        {
            InitializeMonster(monster);
            _combatants.Add(monster);
        }

        private int? GetMonsterIndex(string monsterName)
        {
            IEnumerable<IMonster> combatantsOfTheSameType = GetMonstersWithSameName(monsterName);
            if ( combatantsOfTheSameType.Count() > 0 )
            {
                int maxIndex = combatantsOfTheSameType.Max(m => m.Index.GetValueOrDefault(1));
                return maxIndex + 1;
            }
            return null;
        }

        private IEnumerable<IMonster> GetMonstersWithSameName(string monsterName)
        {
            return from previousInstanceOfCombatant in _combatants
                   where previousInstanceOfCombatant.Name == monsterName
                   select previousInstanceOfCombatant as IMonster;
        }

        private void InitializeMonster(IMonster monster)
        {
            monster.Index = GetMonsterIndex(monster.Name);
            monster.HitPointsCurrent = monster.HitPointsMaximum;
            monster.HitPointsTemporary = 0;
            monster.Conditions = string.Empty;
        }

        private void RefreshViewCombatants()
        {
            SortCombatants();
            _view.Combatants = _combatants.ToArray();
        }

        private void SortCombatants()
        {
            _combatants = ( from selectedCombatant in _combatants
                            orderby selectedCombatant.InitiativeScore descending , selectedCombatant.InitiativeModifier descending ,
                                selectedCombatant.DisplayName
                            select selectedCombatant ).ToList();
        }

        private void StoreReferenceToCurrentCombatantToFaciliteUpdatingOnNextChange(ICombatant combatant)
        {
            PreviouslySelectedCombatant = combatant;
        }

        private void UpdateViewWithCombatantData(ICombatant selectedCombatant)
        {
            _view.SelectedCombatantDisplayEnabled = !string.IsNullOrEmpty(selectedCombatant.Name);
            _view.SelectedCombatantName = selectedCombatant.DisplayName;
            _view.SelectedCombatantInitiativeScore = selectedCombatant.InitiativeScore;
            _view.SelectedCombatantConditions = selectedCombatant.Conditions;
            _view.SelectedCombatantSpecialQualities = selectedCombatant.SpecialQualities;
            _view.SelectedCombatantTactics = selectedCombatant.Tactics;
            _view.SelectedCombatantCurrentHitPoints = selectedCombatant.HitPointsCurrent;
            _view.SelectedCombatantTemporaryHitPoints = selectedCombatant.HitPointsTemporary;
            IPlayerCharacter playerCharacter = selectedCombatant as IPlayerCharacter;
            if ( playerCharacter != null )
            {
                _view.SelectedCombatantAttackPowers = string.Format("At-Will:{0}{1}{0}{0}Encounter:{0}{2}{0}{0}Daily:{0}{3}", Environment.NewLine,
                                                                    playerCharacter.PowersAtWill, playerCharacter.PowersEncounter,
                                                                    playerCharacter.PowersDaily);
                _view.SelectedCombatantCurrentHealingSurges = playerCharacter.HealingSurgesCurrent;
            }
            IMonster monster = selectedCombatant as IMonster;
            if ( monster != null )
            {
                string attackPowers = monster.AttackPowers;
                if ( attackPowers == null )
                {
                    _view.SelectedCombatantAttackPowers = string.Empty;
                }
                else
                {
                    _view.SelectedCombatantAttackPowers = attackPowers.Replace("\n", "\r\n");
                }
                _view.SelectedCombatantCurrentHealingSurges = 0;
            }
            StoreReferenceToCurrentCombatantToFaciliteUpdatingOnNextChange(selectedCombatant);
        }
    }
}